#ifndef VIDEODRIVER_H
#define VIDEODRIVER_H

#include "CommonTypes.h"
#include "Colors.h"
#include "DisplayModes.h"
#include "DeviceCaps.h"
#include "Window.h"
#include "SpriteBatch.h"
#include "VideoSettings.h"
#include "TextureManager.h"
#include "GpuBufferManager.h"
#include "EffectManager.h"
#include "Renderer.h"
#include "RenderingParams.h"

/// Main engine namespace
namespace Cvekas {

/// Video device
class VideoDevice : public boost::noncopyable
{
public:
	/// Constructor
	VideoDevice();
	/// Destructor
	~VideoDevice();

	/// Returns D3DPtr. For internal use.
	D3DPtr getD3D() const { return direct3d; };
	/// Returns D3DDevicePtr. For internal use.
	D3DDevicePtr getD3DDevice() const { return device; };
	
	/// Returns DisplayModes of this device 
	DisplayModesPtr getModes();
	/// Returns DeviceCaps
	DeviceCapsPtr getCaps(D3DDEVTYPE device_type);
	
	/// Initializes Direct3D device
	/// \param window Window which will be associated with this device
	void init(WindowPtr window);

	/// Returns VideoSettings used to initialize this class
	VideoSettings& getSettings();
	/// Returns Window associated with this device
	WindowPtr getWindow() { return window; };
	/// Sets color to fill screen at the beginning of every frame
	void setClearColor(const Color& color);

	/// Returns viewport size of default render target in pixels
	void getViewport(uint& width, uint& height);

	/// Switches to new video mode (which is set in global VideoSettings instance).
	/// Can be used inside beginScene(), endScene() block.
	void switchMode();

	/// Checks if device is available (not lost). Should be called at the beginning of every frame, and
	/// in case of false result all rendering must be skipped.
	bool ensureDevice();
	
	/// Begins rendering of new scene (frame) and clears rendertargets.
	void beginScene(bool clear_color = true, bool clear_depth = true, bool clear_stencil = false,
		RenderTargetPtr render_target = RenderTargetPtr());
	/// Ends renderings of scene (frame) and presents results to the screen.
	void endScene();

	/// Returns TextureManager
	TextureManagerPtr getTextureManager() const;

	/// Returns GpuBufferManaer
	GpuBufferManagerPtr getGpuBufferManager() const;

	/// Returns EffectManager
	EffectManagerPtr getEffectManager() const;

	/// Creates new SpriteBatch structure
	SpriteBatchPtr createSpriteBatch(RendererPtr renderer, uint virtual_width, uint virtual_height, uint16 priority);

	/// Creates new Renderer
	RendererPtr createRenderer();

	/// Returns RenderingParams object used to provide common info to effects
	RenderingParams& getRenderingParams() { return rendering_params; };

	/// Sets scene projection matrix. Must be called before beginScene() to use new matrix in current frame.
	void setProjectionMatrix(const Matrix& projection) { projection_matrix = projection; };

	/// Returns currently active projection matrix. 
	/// This might not be the same as you set using setProjectionMatrix() if you done it in beginScene/endScene() block.
	const Matrix& getProjectionMatrix() const;

	/// Sets screen aspect ratio. Image will be letterboxed if current resolution has different aspect ratio
	void setAspectRatio(float ratio);

	/// Resets device. Can be used to switch display modes.
	void reset();
	
private:
	void deviceLost();
	void deviceReset();
	void setUpParams();

	D3DPtr direct3d;
	D3DDevicePtr device;

	WindowPtr window;

	DisplayModesPtr display_modes;
	DeviceCapsPtr device_caps;
	D3DPRESENT_PARAMETERS params;
	D3DXCOLOR clear_color;

	TextureManagerPtr texture_manager;
	GpuBufferManagerPtr gpu_buffer_manager;
	EffectManagerPtr effect_manager;

	RenderingParams rendering_params;
	Matrix projection_matrix;

	IDirect3DSurface9* default_rt;
	IDirect3DSurface9* default_ds;

	D3DVIEWPORT9 viewport;

	bool lost;
	bool switch_mode;
	bool in_scene_block;
	bool frame_ended;
};

typedef boost::shared_ptr<VideoDevice> VideoDevicePtr;

} // namespace

#endif